#!/usr/bin/perl -w

##HACKABOT_HELP##
# Play hangman in an IRC channel.
# !hangman [ a | new | #chan word/phrase (in privmsg only) ]
##HACKABOT_HELP##

use strict;
use DBI;
use XML::Simple;

my $CONFIG = XMLin($ENV{'HACKABOT_CFG'});
my $DBCFG = $CONFIG->{'cmdconfig'}->{'database'};

my $dbhost = $DBCFG->{'host'};
my $dbname = $DBCFG->{'name'};
my $dbuser = $DBCFG->{'user'};
my $dbpass = $DBCFG->{'pass'};

my ($msg, $type, $chan, $nick);
while (<>) {
	if (/^type\s+(\S+)/) {
		$type = $1;
	}
	elsif (/^to\s+(#\S+)/) {
		$chan = $1;
	}
	elsif (/^nick\s+(\S+)/) {
		$nick = $1;
	}
	elsif (/^msg\s+(.*)\s*$/) {
		$msg = $1;
	}
}

if ($type eq "privmsg") {
	$chan = $nick;
}

if (not defined $chan) {
	die "hangman: uh, chan is not defined?!?\n";
}

my ($dbh, $sth);

if ($type eq "privmsg" and $msg =~ /^(#\S+)\s+(\S+.*)$/) {
	my $destchan = $1;
	my $phrase = $2;

	#if (not $phrase =~ /^[a-z ]+$/i) {
	#	print "send Only phrases containing letters are allowed.";
	#	exit;
	#}

	$dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass, { PrintError => 0});
	if (!$dbh) {
		die "hangman: Failed to connect to database\n";
	}

	my $qdestchan = $dbh->quote($destchan);
	my $qphrase = $dbh->quote($phrase);
	my $qnick = $dbh->quote($nick);

	$dbh->do("INSERT INTO hangman SET chan = $qdestchan, state = 1, "
		."phrase = $qphrase, nick = $qnick ")
		or $dbh->do("UPDATE hangman SET "
		."phrase = $qphrase, nick = $qnick "
		."WHERE chan = $qdestchan AND state = 1")
		or die("hangman: db update failed\n");
	$dbh->disconnect;

	print "send Next phrase for $destchan set to $phrase\n";
	print "to $destchan\n";
	print "action is ready with the next hangman from $nick\n";
}
elsif ($msg =~ /^new$/) {
	# New game

	$dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass, { PrintError => 1 });
	if (!$dbh) {
		die "hangman: Failed to connect to database\n";
	}

	my $qchan = $dbh->quote($chan);

	$sth = $dbh->prepare("SELECT phrase FROM hangman "
		."WHERE chan = $qchan AND state = 1");
	$sth->execute;
	my ($row, $phrase, $update);
	
	$row = $sth->fetchrow_hashref;
	if (not defined $row->{'phrase'}) {
		$update = 0;
		$phrase = getrandom("$ENV{'HACKABOT_DIR'}/misc/wordlist");
	}
	else {
		$update = 1;
		$phrase = $row->{'phrase'};
	}
	$sth->finish();
	my $guess = $phrase;
	$guess =~ s/[a-z]/_/ig;
	my $qphrase = $dbh->quote($phrase);
	my $qguess = $dbh->quote($guess);

	$dbh->do("DELETE FROM hangman WHERE chan = $qchan AND state = 0");
	if ($update) {
		$dbh->do("UPDATE hangman SET state = 0, "
		."guess = $qguess WHERE chan = $qchan AND state = 1")
		or die("hangman: db update failed\n");
	}
	else {
		$dbh->do("INSERT INTO hangman SET chan = $qchan, state = 0, "
		."phrase = $qphrase, guess = $qguess")
		or die("hangman: db update failed\n");
	}

	print "send New Hangman Game!\n";
	print "sendnext\n";
	print "    $guess\n";

	$dbh->disconnect;
}
elsif ($msg =~ /^([a-z])$/i) {
	# Guess
	my $char = $1;
	$dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass, { PrintError => 1 });
	if (!$dbh) {
		die "hangman: Failed to connect to database\n";
	}

	my $qchan = $dbh->quote($chan);

	$sth = $dbh->prepare("SELECT phrase, guess, wrong, final FROM hangman "
		."WHERE chan = $qchan AND state = 0");
	$sth->execute;
	my $row;
	
	$row = $sth->fetchrow_hashref;
	if (not defined $row->{'guess'} or not defined $row->{'phrase'}) {
		print "send No hangman game.";
		exit;
	}
	$sth->finish;
	my $phrase = $row->{'phrase'};
	my $guess = $row->{'guess'};
	my $wrong = $row->{'wrong'};

	if (defined $row->{'final'}) {
		print "sendnext\n";
		print getshow($guess, $wrong, $row->{'final'});
		exit;
	}

	if ($guess =~ /$char/i or $wrong =~ /$char/i) {
		print "send $char has already been guessed.";
		exit;
	}

	if ($phrase =~ /$char/i) {
		for (my $i = 0; $i < length($phrase); $i++) {
			my $a = substr($phrase, $i, 1);
			if ($a =~ /$char/i) {
				substr($guess, $i, 1, $a);
			}
		}
	}
	else {
		if (length($wrong) > 0) {
			$wrong .= ",";
		}
		$wrong .= lc($char);
	}

	my $final;
	if ($guess eq $phrase) {
		$final = "'win'";
	}
	elsif (getwrong($wrong) >= 6) {
		$final = "'lose'";
		$guess = $phrase;
	}
	else {
		$final = "NULL";
	}
		
	my $qwrong = $dbh->quote($wrong);
	my $qguess = $dbh->quote($guess);
	$dbh->do("UPDATE hangman SET guess = $qguess, wrong = $qwrong, "
		."final = $final WHERE chan = $qchan AND state = 0")
		or die("hangman: db update failed\n");
	
	print "sendnext\n";
	print getshow($guess, $wrong, ($final eq "NULL")? undef : $final);
	
	$dbh->disconnect;
}
else {
	# Show
	$dbh = DBI->connect("DBI:mysql:$dbname:$dbhost", $dbuser, $dbpass, { PrintError => 1 });
	if (!$dbh) {
		die "hangman: Failed to connect to database\n";
	}

	my $qchan = $dbh->quote($chan);

	$sth = $dbh->prepare("SELECT phrase, guess, wrong, final FROM hangman "
		."WHERE chan = $qchan AND state = 0");
	$sth->execute;
	my ($row, $phrase, $update);
	
	$row = $sth->fetchrow_hashref;
	$sth->finish;
	if (defined $row->{'phrase'} and not defined $row->{'final'} and
			lc($row->{'phrase'}) eq lc($msg)) {
		$row->{'guess'} = $row->{'phrase'};
		$row->{'final'} = "'win'";
		
		my $qguess = $dbh->quote($row->{'guess'});
		$dbh->do("UPDATE hangman SET guess = $qguess, "
			."final = 'win' WHERE chan = $qchan AND state = 0")
			or die("hangman: db update failed\n");
	}
	elsif (defined $row->{'phrase'} and
			length($msg) != length($row->{'phrase'})) {
		print "send Your guess had ".length($msg).
			" characters but the phrase has ".
			length($row->{'phrase'})."! :-P\n";
	}
	elsif (defined $row->{'phrase'} and length($msg)) {
		$row->{'guess'} = $row->{'phrase'};
		$row->{'final'} = "'lose'";
		
		my $qguess = $dbh->quote($row->{'guess'});
		$dbh->do("UPDATE hangman SET guess = $qguess, "
			."final = 'lose' WHERE chan = $qchan AND state = 0")
			or die("hangman: db update failed\n");
	}	
	if (defined $row->{'guess'} and defined $row->{'wrong'}) {
		print "sendnext\n";
		print getshow($row->{'guess'}, $row->{'wrong'}, $row->{'final'});
	}
	else {
		print "send No hangman game.";
	}
	
	$dbh->disconnect;
}

sub getrandom {
	my $file = shift;

	my $line;
	open(DICT, $file);
	rand($.) < 1 && ($line = $_) while <DICT>;
	close(DICT);

	chomp($line);
	return $line;
}

sub getshow {
	my $guess = shift;
	my $wrong = shift;
	my $final = shift;

	my $num = getwrong($wrong);

	if (defined $final) {
		$final =~ s/.*win.*/Game Over: You WIN!/;
		$final =~ s/.*lose.*/Game Over: You LOSE!/;
	}

	my $result = "";
	$result .= ($num >= 1)? " O " : "   ";
	$result .= " $guess\n";
	if (length($wrong) > 0 or $num > 1) {
		$result .= ($num >= 3)? "/" : " ";
		$result .= ($num >= 2)? "|" : " ";
		$result .= ($num >= 4)? "\\" : " ";
		$result .= " $wrong\n";
	}
	if (defined $final or $num >= 5) {
		$result .= ($num >= 5)? "/" : " ";
		$result .= ($num >= 6)? " \\" : "  ";
		$result .= (defined $final)? " $final" : "";
		$result .= "\n";
	}

	return $result;
}

sub getwrong {
	my $wrong = shift;

	my $w = $wrong;
	$w =~ s/[^A-Z]//ig;
	return length($w);
}

